package com.conference.service.impl;

import com.conference.entity.Conference;
import com.conference.repository.ConferenceRepository;
import com.conference.service.ConferenceService;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true)
public class ConferenceServiceImpl implements ConferenceService {

    @Autowired
    private ConferenceRepository conferenceRepository;

    @Value("${app.base-url}")
    private String baseUrl;

    @Override
    @Transactional
    public Conference createConference(Conference conference) {
        // 生成会议通知
        conference.setNoticeContent(generateNotice(conference));
        Conference save = conferenceRepository.save(conference);
        // 生成二维码
        conference.setQrCodeUrl(generateQRCode(conference));
        return save;
    }

    @Override
    @Transactional
    public Conference updateConference(Conference conference) {
        // 更新时重新生成通知和二维码
        conference.setNoticeContent(generateNotice(conference));
        conference.setQrCodeUrl(generateQRCode(conference));
        return conferenceRepository.save(conference);
    }

    @Override
    @Transactional
    public void deleteConference(Long id) {
        conferenceRepository.deleteById(id);
    }

    @Override
    public Conference getConferenceById(Long id) {
        return conferenceRepository.findById(id).orElse(null);
    }

    @Override
    public Page<Conference> getAllConferences(Pageable pageable) {
        return conferenceRepository.findAll(pageable);
    }

    @Override
    public long getTotalConferences() {
        return conferenceRepository.count();
    }

    @Override
    public long getPublishedConferences() {
        return conferenceRepository.countByStatus(1);
    }

    @Override
    public long getUpcomingConferences() {
        LocalDateTime now = LocalDateTime.now();
        return conferenceRepository.countByStatusAndStartTimeAfter(1, now);
    }

    @Override
    public Map<String, Long> getConferenceStatusDistribution() {
        Map<String, Long> distribution = new HashMap<>();
        distribution.put("DRAFT", conferenceRepository.countByStatus(0));
        distribution.put("PUBLISHED", conferenceRepository.countByStatus(1));
        distribution.put("ENDED", conferenceRepository.countByStatus(2));
        return distribution;
    }

    @Override
    public List<Map<String, Object>> getVenueUsageStatistics() {
        return conferenceRepository.findVenueUsageStatistics();
    }

    @Override
    public List<Map<String, Object>> getMonthlyConferenceCount() {
        LocalDateTime endDate = LocalDateTime.now();
        LocalDateTime startDate = endDate.minusMonths(11).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
        return conferenceRepository.findMonthlyConferenceCount(startDate, endDate);
    }

    @Override
    public Page<Conference> searchConferences(
        String title,
        String location,
        Integer status,
        LocalDate startDate,
        LocalDate endDate,
        Pageable pageable
    ) {
        return conferenceRepository.findAll((Specification<Conference>) (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (title != null && !title.isEmpty()) {
                predicates.add(cb.like(cb.lower(root.get("title")), "%" + title.toLowerCase() + "%"));
            }

            if (location != null && !location.isEmpty()) {
                predicates.add(cb.like(cb.lower(root.get("location")), "%" + location.toLowerCase() + "%"));
            }

            if (status != null) {
                predicates.add(cb.equal(root.get("status"), status));
            }

            if (startDate != null) {
                predicates.add(cb.greaterThanOrEqualTo(root.get("startTime"), startDate.atStartOfDay()));
            }

            if (endDate != null) {
                predicates.add(cb.lessThanOrEqualTo(root.get("endTime"), endDate.atTime(23, 59, 59)));
            }

            return cb.and(predicates.toArray(new Predicate[0]));
        }, pageable);
    }

    @Override
    public String generateNotice(Conference conference) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
        return String.format(
            "会议通知\n\n" +
            "会议主题：%s\n" +
            "会议时间：%s 至 %s\n" +
            "会议地点：%s\n" +
            "会议描述：%s\n\n" +
            "请准时参加。\n" +
            "扫描下方二维码可查看会议详情。",
            conference.getTitle(),
            conference.getStartTime().format(formatter),
            conference.getEndTime().format(formatter),
            conference.getLocation(),
            conference.getDescription()
        );
    }

    @Override
    public String generateQRCode(Conference conference) {
        try {
            String url = baseUrl + "/mobile/" + conference.getId();
            QRCodeWriter qrCodeWriter = new QRCodeWriter();
            BitMatrix bitMatrix = qrCodeWriter.encode(url, BarcodeFormat.QR_CODE, 200, 200);
            
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
            
            return "data:image/png;base64," + Base64.getEncoder().encodeToString(outputStream.toByteArray());
        } catch (WriterException | IOException e) {
            throw new RuntimeException("生成二维码失败", e);
        }
    }
} 